package com.tobeface.wechat.web;

import com.tobeface.modules.lang.Langs;
import com.tobeface.modules.lang.Predicate;
import com.tobeface.wechat.core.WeChatSettings;
import com.tobeface.wechat.message.*;
import com.tobeface.wechat.message.annotation.DefaultMessageHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * @author loudyn
 */
public class WeChatSpringFilter extends OncePerRequestFilter {

    private static final String WECHAT_MESSAGE_CONTENT_TYPE = "text/xml";

    private WeChatMessageDispatcher weChatMessageDispatcher;

    @Override
    protected void initFilterBean() throws ServletException {
        super.initFilterBean();

        WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        Map<String, WeChatMessageDispatcher> dispatchers = wac.getBeansOfType(WeChatMessageDispatcher.class);
        if (!dispatchers.isEmpty()) {
            weChatMessageDispatcher = wac.getBean(WeChatMessageDispatcher.class);
            return;
        }

        Map<String, WeChatSettings> settingsMap = wac.getBeansOfType(WeChatSettings.class);
        Map<String, WeChatMessageHandler> messageHandlerMap = wac.getBeansOfType(WeChatMessageHandler.class);
        if (settingsMap.isEmpty() || messageHandlerMap.isEmpty()) {
            throw new WeChatMessageDispatcherNotFoundException();
        }

        WeChatSettings settings = wac.getBean(WeChatSettings.class);
        List<WeChatMessageHandler> messageHandlers = Langs.aslist(messageHandlerMap.values());
        List<WeChatMessageHandler> notDefaultMessageHandlers = Langs.filter(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return !input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });

        weChatMessageDispatcher = new DefaultWeChatMessageDispatcher(settings, notDefaultMessageHandlers);
        if (hasDefaultMessageHandler(messageHandlers)) {
            configDefaultMessageHandler(weChatMessageDispatcher, messageHandlers);
        }
    }

    private boolean hasDefaultMessageHandler(List<WeChatMessageHandler> messageHandlers) {
        return Langs.any(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });
    }

    private void configDefaultMessageHandler(WeChatMessageDispatcher weChatMessageDispatcher,
                                             List<WeChatMessageHandler> messageHandlers) {
        List<WeChatMessageHandler> defaultMessageHandlers = Langs.filter(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });
        if (defaultMessageHandlers.size() > 1) {
            throw new MultiDefaultMessageHandlerException(defaultMessageHandlers);
        }
        ((DefaultWeChatMessageDispatcher) weChatMessageDispatcher).setDefaultMessageHandler(defaultMessageHandlers.get(0));
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.setContentType(WECHAT_MESSAGE_CONTENT_TYPE);
        WeChatMessageContext context = WeChatHttpMessageContexts.ofMessageContext(request, response);
        weChatMessageDispatcher.dispatchMessage(context);
    }
}
